/**
 * Copyright (C) 2012 Dmitriy Kukharev
 *  
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *  
 *      http://www.apache.org/licenses/LICENSE-2.0
 *  
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package ru.nsu.weather.net;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;

import org.xmlpull.v1.XmlPullParserException;

import ru.nsu.weather.data.Weather;
import ru.nsu.weather.net.DownloadWeatherTask.Parameters;
import ru.nsu.weather.net.DownloadWeatherTask.Result;
import android.os.AsyncTask;
import android.util.Log;

/**
 * DownloadWeatherTask downloads xml data from 
 * <a href="http://weather.nsu.ru">http://weather.nsu.ru</a> site, parses it and 
 * represents as {@link Weather} object.
 * @author Dmitriy Kukharev
 *
 */
public class DownloadWeatherTask extends AsyncTask<Parameters, Void, Result> {
	// debug tag
	private static final String DEBUG_TAG = DownloadWeatherTask.class.getName();
	// XML data endpoint
	private static final String WEATHER_URL = "http://weather.nsu.ru/xml.php";
	
	// temperature listener
	private TemperatureListener temperatureListener;
	
	/**
	 * Creates a new DownloadWeatherTask
	 * @param listener TemperatureListener
	 * @see TemperatureListener
	 */
	public DownloadWeatherTask(TemperatureListener listener) {
		super();
		temperatureListener = listener;
	}

	@Override
	protected Result doInBackground(Parameters... params) {
		try {
			String url = params[0].createUrl();
			return new Result(downloadWeather(url));
		} catch (Throwable e) {
			Log.e(DEBUG_TAG, "An error occured while downloading the weather data", e);
			return new Result(e);
		}
	}

	@Override
	protected void onPostExecute(Result result) {
		super.onPostExecute(result);
		
		if (temperatureListener == null)
			return;
		
		if (result.isSuccess()) {
			temperatureListener.weatherChanged(result.getResult());
		} else {
			temperatureListener.measurementFailed(result.getError());
		}
	}

	// Given a URL, establishes an HttpUrlConnection and retrieves
	// the web page content as a InputStream, which it returns as
	// a string.
	private Weather downloadWeather(String myurl) throws IOException, XmlPullParserException {
		InputStream is = null;
		Log.d(DEBUG_TAG, "Weather url requested: " + myurl);
		try {
			URL url = new URL(myurl);
			HttpURLConnection conn = (HttpURLConnection) url.openConnection();
			conn.setReadTimeout(10000 /* milliseconds */);
			conn.setConnectTimeout(15000 /* milliseconds */);
			conn.setRequestMethod("GET");
			conn.setDoInput(true);
			conn.connect();
			int response = conn.getResponseCode();
			Log.d(DEBUG_TAG, "The response is: " + response);
			is = conn.getInputStream();

			return new WeatherParser().parse(is);
			// Makes sure that the InputStream is closed after the app is
			// finished using it.
		} finally {
			if (is != null) {
				is.close();
			} 
		}
	}
	
	public static class Parameters {
		private static final int TYPE_3 = 1;
		private static final int TYPE_CUSTOM = 2;
		
		private int type;
		
		private long start;
		private long stop;
		
		public Parameters() {
			type = TYPE_3;
		}
		
		public Parameters(long start, long stop) {
			if (start >= stop)
				throw new IllegalArgumentException("Start parameter must be less than stop parameter");
			type = TYPE_CUSTOM;
			this.start = start;
			this.stop = stop;
		}
		
		private String createUrl() {
			StringBuffer stringBuffer = new StringBuffer(WEATHER_URL);
			
			if (type == TYPE_CUSTOM) {
				stringBuffer.append("?std=various&start=");
				stringBuffer.append(start);
				stringBuffer.append("&end=");
				stringBuffer.append(stop);
			}
			
			return stringBuffer.toString();
		}
	}
	
	/**
	 * Weather result object. Holds either result or error. In case when 
	 * <code>isSuccess</code> return <code>false</code> its <code>getResult</code>
	 * method returns <code>null</code>. When <code>isSuccess</code> returns <code>true</code>
	 * <code>getResult</code> returns actual result: <code>Weather</code> object.
	 * @author Dmitriy Kukharev
	 */
	public static class Result {
		private Weather result;
		private Throwable error;
		private boolean success;

		/**
		 * Create successful result
		 * @param result Weather object represents result
		 */
		public Result(Weather result) {
			success = true;
			this.result = result;
		}

		/**
		 * Create failure result
		 * @param error Throwable
		 */
		public Result(Throwable error) {
			success = false;
			this.error = error;
		}

		/**
		 * Returns weather snapshot. In case when {@link #isSuccess()} returns 
		 * false this method returns null
		 * @return weather snapshot
		 */
		public Weather getResult() {
			return result;
		}

		/**
		 * Returns error. In case when {@link #isSuccess()} returns true
		 * this method returns null
		 * @return error
		 */
		public Throwable getError() {
			return error;
		}

		/**
		 * Returns true if task executed successfully and false otherwise
		 * @return boolean
		 */
		public boolean isSuccess() {
			return success;
		}
	}
}
